//! Module for converting CSS types to Rust code

#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use alloc::{collections::btree_map::BTreeMap, format, string::String, vec::Vec};
use core::hash::Hash;

use crate::{
    corety::*,
    css::*,
    props::{basic::*, layout::*, property::*, style::*},
};

// Trait for formatting types as Rust code
pub trait FormatAsRustCode {
    fn format_as_rust_code(&self, tabs: usize) -> String;
}

// Trait for getting hash of types
pub trait GetHash {
    fn get_hash(&self) -> u64;
}

impl<T: Hash> GetHash for T {
    fn get_hash(&self) -> u64 {
        use highway::{HighwayHash, HighwayHasher, Key};
        let mut hasher = HighwayHasher::new(Key([0; 4]));
        self.hash(&mut hasher);
        hasher.finalize64()
    }
}

// In order to generate the Rust code, all items that implement Drop
// have to be declared before being used.
#[derive(Default)]
pub struct VecContents {
    // the u64 is the hash of the type (generated by string.get_hash())
    pub strings: BTreeMap<u64, AzString>,
    pub style_filters: BTreeMap<u64, StyleFilterVec>,
    pub style_background_sizes: BTreeMap<u64, StyleBackgroundSizeVec>,
    pub style_background_repeats: BTreeMap<u64, StyleBackgroundRepeatVec>,
    pub style_background_contents: BTreeMap<u64, StyleBackgroundContentVec>,
    pub style_background_positions: BTreeMap<u64, StyleBackgroundPositionVec>,
    pub style_transforms: BTreeMap<u64, StyleTransformVec>,
    pub font_families: BTreeMap<u64, StyleFontFamilyVec>,
    pub linear_color_stops: BTreeMap<u64, NormalizedLinearColorStopVec>,
    pub radial_color_stops: BTreeMap<u64, NormalizedRadialColorStopVec>,
}

impl VecContents {
    pub fn format(&self, tabs: usize) -> String {
        let mut result = String::new();
        let t = "    ".repeat(tabs);
        let t2 = "    ".repeat(tabs + 1);

        for (key, item) in self.strings.iter() {
            result.push_str(&format!(
                "\r\n    const STRING_{}: AzString = AzString::from_const_str(\"{}\");",
                key,
                item.as_str()
            ));
        }

        for (key, item) in self.style_filters.iter() {
            let val = item
                .iter()
                .map(|filter| format_style_filter(filter, tabs + 1))
                .collect::<Vec<_>>()
                .join(&format!(",\r\n{}", t));

            result.push_str(&format!(
                "\r\n    const STYLE_FILTER_{}_ITEMS: &[StyleFilter] = &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.style_background_sizes.iter() {
            let val = item
                .iter()
                .map(|bgs| format_style_background_size(bgs))
                .collect::<Vec<_>>()
                .join(&format!(",\r\n{}", t));

            result.push_str(&format!(
                "\r\n    const STYLE_BACKGROUND_SIZE_{}_ITEMS: &[StyleBackgroundSize] = \
                 &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.style_background_repeats.iter() {
            let val = item
                .iter()
                .map(|bgr| bgr.format_as_rust_code(tabs + 1))
                .collect::<Vec<_>>()
                .join(&format!(",\r\n{}", t));

            result.push_str(&format!(
                "\r\n    const STYLE_BACKGROUND_REPEAT_{}_ITEMS: &[StyleBackgroundRepeat] = \
                 &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.style_background_contents.iter() {
            let val = item
                .iter()
                .map(|bgc| format_style_background_content(bgc, tabs + 1))
                .collect::<Vec<_>>()
                .join(&format!(",\r\n{}", t));

            result.push_str(&format!(
                "\r\n    const STYLE_BACKGROUND_CONTENT_{}_ITEMS: &[StyleBackgroundContent] = \
                 &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.style_background_positions.iter() {
            let val = item
                .iter()
                .map(|bgp| format_style_background_position(bgp, tabs))
                .collect::<Vec<_>>()
                .join(&format!(",\r\n{}", t));

            result.push_str(&format!(
                "\r\n    const STYLE_BACKGROUND_POSITION_{}_ITEMS: &[StyleBackgroundPosition] = \
                 &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.style_transforms.iter() {
            let val = format_style_transforms(item.as_ref(), tabs + 1);

            result.push_str(&format!(
                "\r\n    const STYLE_TRANSFORM_{}_ITEMS: &[StyleTransform] = &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.font_families.iter() {
            let val = format_font_ids(item.as_ref(), tabs + 1);

            result.push_str(&format!(
                "\r\n    const STYLE_FONT_FAMILY_{}_ITEMS: &[StyleFontFamily] = &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.linear_color_stops.iter() {
            let val = format_linear_color_stops(item.as_ref(), 1);

            result.push_str(&format!(
                "\r\n    const LINEAR_COLOR_STOP_{}_ITEMS: &[NormalizedLinearColorStop] = \
                 &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        for (key, item) in self.radial_color_stops.iter() {
            let val = format_radial_color_stops(item.as_ref(), tabs);

            result.push_str(&format!(
                "\r\n    const RADIAL_COLOR_STOP_{}_ITEMS: &[NormalizedRadialColorStop] = \
                 &[\r\n{}{}\r\n{}];",
                key, t2, val, t
            ));
        }

        result
    }

    // given a CSS property, clones all the necessary strings (see class documentation)
    pub fn insert_from_css_property(&mut self, prop: &CssProperty) {
        match prop {
            CssProperty::FontFamily(CssPropertyValue::Exact(v)) => {
                for family in v.iter() {
                    match family {
                        StyleFontFamily::System(s) => {
                            // if the font-family is surrounded by quotes, strip them ("Arial" ->
                            // Arial)
                            let s = s.as_str();
                            let s = s.trim();
                            let s = s.trim_start_matches('\"');
                            let s = s.trim_end_matches('\"');
                            let s = s.trim_start_matches('\'');
                            let s = s.trim_end_matches('\'');

                            self.strings.insert(s.get_hash(), s.to_string().into());
                        }
                        StyleFontFamily::File(s) => {
                            let s = s.as_str();
                            let s = s.trim();
                            let s = s.trim_start_matches('\"');
                            let s = s.trim_end_matches('\"');
                            let s = s.trim_start_matches('\'');
                            let s = s.trim_end_matches('\'');

                            self.strings.insert(s.get_hash(), s.to_string().into());
                        }
                        _ => {}
                    }
                }
                self.font_families.insert(v.get_hash(), v.clone());
            }
            CssProperty::Transform(CssPropertyValue::Exact(v)) => {
                self.style_transforms.insert(v.get_hash(), v.clone());
            }
            CssProperty::BackgroundRepeat(CssPropertyValue::Exact(v)) => {
                self.style_background_repeats
                    .insert(v.get_hash(), v.clone());
            }
            CssProperty::BackgroundSize(CssPropertyValue::Exact(v)) => {
                self.style_background_sizes.insert(v.get_hash(), v.clone());
            }
            CssProperty::BackgroundPosition(CssPropertyValue::Exact(v)) => {
                self.style_background_positions
                    .insert(v.get_hash(), v.clone());
            }
            CssProperty::BackgroundContent(CssPropertyValue::Exact(v)) => {
                for background in v.iter() {
                    match background {
                        StyleBackgroundContent::Image(id) => {
                            self.strings.insert(id.get_hash(), id.clone());
                        }
                        StyleBackgroundContent::LinearGradient(lg) => {
                            self.linear_color_stops
                                .insert(lg.stops.get_hash(), lg.stops.clone());
                        }
                        StyleBackgroundContent::RadialGradient(rg) => {
                            self.linear_color_stops
                                .insert(rg.stops.get_hash(), rg.stops.clone());
                        }
                        StyleBackgroundContent::ConicGradient(lg) => {
                            self.radial_color_stops
                                .insert(lg.stops.get_hash(), lg.stops.clone());
                        }
                        _ => {}
                    }
                }
                self.style_background_contents
                    .insert(v.get_hash(), v.clone());
            }
            CssProperty::Filter(CssPropertyValue::Exact(v)) => {
                self.style_filters.insert(v.get_hash(), v.clone());
            }
            CssProperty::BackdropFilter(CssPropertyValue::Exact(v)) => {
                self.style_filters.insert(v.get_hash(), v.clone());
            }
            _ => {}
        }
    }
}

// Helper functions for formatting values

pub fn format_pixel_value(p: &PixelValue) -> String {
    let value = p.number.get();

    // Extract integer and fractional parts
    // For example: 1.5 -> pre_comma=1, post_comma=50
    //              0.83 -> pre_comma=0, post_comma=83
    //              2.0 -> pre_comma=2, post_comma=0
    let pre_comma = libm::floorf(value) as isize;
    let fraction = value - pre_comma as f32;
    // Multiply by 100 to get two decimal places, then round
    let post_comma = libm::roundf(fraction * 100.0) as isize;

    match p.metric {
        SizeMetric::Px => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Px, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Pt => format!(
            "PixelValue::const_pt_fractional({}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Em => format!(
            "PixelValue::const_em_fractional({}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Rem => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Rem, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Percent => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Percent, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::In => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::In, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Cm => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Cm, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Mm => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Mm, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Vw => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Vw, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Vh => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Vh, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Vmin => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Vmin, {}, {})",
            pre_comma, post_comma
        ),
        SizeMetric::Vmax => format!(
            "PixelValue::const_from_metric_fractional(SizeMetric::Vmax, {}, {})",
            pre_comma, post_comma
        ),
    }
}

pub fn format_pixel_value_no_percent(p: &PixelValueNoPercent) -> String {
    format!(
        "PixelValueNoPercent {{ inner: {} }}",
        format_pixel_value(&p.inner)
    )
}

pub fn format_float_value(f: &FloatValue) -> String {
    format!("FloatValue::const_new({})", libm::roundf(f.get()) as isize)
}

pub fn format_percentage_value(f: &PercentageValue) -> String {
    format!(
        "PercentageValue::const_new({})",
        libm::roundf(f.normalized() * 100.0) as isize
    )
}

pub fn format_angle_value(f: &AngleValue) -> String {
    format!(
        "AngleValue::const_deg({})",
        libm::roundf(f.to_degrees()) as isize
    )
}

pub fn format_color_value(c: &ColorU) -> String {
    format!(
        "ColorU {{ r: {}, g: {}, b: {}, a: {} }}",
        c.r, c.g, c.b, c.a
    )
}

// Macro implementations for common patterns

macro_rules! impl_float_value_fmt {
    ($struct_name:ident) => {
        impl FormatAsRustCode for $struct_name {
            fn format_as_rust_code(&self, _tabs: usize) -> String {
                format!(
                    "{} {{ inner: {} }}",
                    stringify!($struct_name),
                    format_float_value(&self.inner)
                )
            }
        }
    };
}

impl_float_value_fmt!(LayoutFlexGrow);
impl_float_value_fmt!(LayoutFlexShrink);

macro_rules! impl_percentage_value_fmt {
    ($struct_name:ident) => {
        impl FormatAsRustCode for $struct_name {
            fn format_as_rust_code(&self, _tabs: usize) -> String {
                format!(
                    "{} {{ inner: {} }}",
                    stringify!($struct_name),
                    format_percentage_value(&self.inner)
                )
            }
        }
    };
}

impl_percentage_value_fmt!(StyleLineHeight);
impl_percentage_value_fmt!(StyleOpacity);

macro_rules! impl_pixel_value_fmt {
    ($struct_name:ident) => {
        impl FormatAsRustCode for $struct_name {
            fn format_as_rust_code(&self, _tabs: usize) -> String {
                format!(
                    "{} {{ inner: {} }}",
                    stringify!($struct_name),
                    format_pixel_value(&self.inner)
                )
            }
        }
    };
}

impl_pixel_value_fmt!(StyleTabWidth);
impl_pixel_value_fmt!(StyleBorderTopLeftRadius);
impl_pixel_value_fmt!(StyleBorderBottomLeftRadius);
impl_pixel_value_fmt!(StyleBorderTopRightRadius);
impl_pixel_value_fmt!(StyleBorderBottomRightRadius);

impl_pixel_value_fmt!(LayoutBorderTopWidth);
impl_pixel_value_fmt!(LayoutBorderLeftWidth);
impl_pixel_value_fmt!(LayoutBorderRightWidth);
impl_pixel_value_fmt!(LayoutBorderBottomWidth);
impl_pixel_value_fmt!(StyleLetterSpacing);
impl_pixel_value_fmt!(StyleWordSpacing);
impl_pixel_value_fmt!(StyleFontSize);

impl_pixel_value_fmt!(LayoutMarginTop);
impl_pixel_value_fmt!(LayoutMarginBottom);
impl_pixel_value_fmt!(LayoutMarginRight);
impl_pixel_value_fmt!(LayoutMarginLeft);

impl_pixel_value_fmt!(LayoutPaddingTop);
impl_pixel_value_fmt!(LayoutPaddingBottom);
impl_pixel_value_fmt!(LayoutPaddingRight);
impl_pixel_value_fmt!(LayoutPaddingLeft);
impl_pixel_value_fmt!(LayoutPaddingInlineStart);
impl_pixel_value_fmt!(LayoutPaddingInlineEnd);

impl FormatAsRustCode for LayoutWidth {
    fn format_as_rust_code(&self, _tabs: usize) -> String {
        match self {
            LayoutWidth::Auto => "LayoutWidth::Auto".to_string(),
            LayoutWidth::Px(px) => format!("LayoutWidth::Px({})", format_pixel_value(px)),
            LayoutWidth::MinContent => "LayoutWidth::MinContent".to_string(),
            LayoutWidth::MaxContent => "LayoutWidth::MaxContent".to_string(),
        }
    }
}

impl FormatAsRustCode for LayoutHeight {
    fn format_as_rust_code(&self, _tabs: usize) -> String {
        match self {
            LayoutHeight::Auto => "LayoutHeight::Auto".to_string(),
            LayoutHeight::Px(px) => format!("LayoutHeight::Px({})", format_pixel_value(px)),
            LayoutHeight::MinContent => "LayoutHeight::MinContent".to_string(),
            LayoutHeight::MaxContent => "LayoutHeight::MaxContent".to_string(),
        }
    }
}

impl_pixel_value_fmt!(LayoutMinHeight);
impl_pixel_value_fmt!(LayoutMinWidth);
impl_pixel_value_fmt!(LayoutMaxWidth);
impl_pixel_value_fmt!(LayoutMaxHeight);
impl_pixel_value_fmt!(LayoutTop);
impl_pixel_value_fmt!(LayoutBottom);

impl_pixel_value_fmt!(LayoutRight);
impl_pixel_value_fmt!(LayoutLeft);

// LayoutFlexBasis implementation moved to `props/layout/flex.rs`.

impl_pixel_value_fmt!(LayoutColumnGap);
impl_pixel_value_fmt!(LayoutRowGap);

impl_grid_value_fmt!(GridTemplate);
impl_grid_value_fmt!(GridPlacement);

impl_color_value_fmt!(StyleTextColor);
impl_color_value_fmt!(StyleBorderTopColor);
impl_color_value_fmt!(StyleBorderLeftColor);
impl_color_value_fmt!(StyleBorderRightColor);
impl_color_value_fmt!(StyleBorderBottomColor);

impl_enum_fmt!(
    StyleMixBlendMode,
    Normal,
    Multiply,
    Screen,
    Overlay,
    Darken,
    Lighten,
    ColorDodge,
    ColorBurn,
    HardLight,
    SoftLight,
    Difference,
    Exclusion,
    Hue,
    Saturation,
    Color,
    Luminosity
);

impl_enum_fmt!(StyleHyphens, Auto, None);

impl_enum_fmt!(StyleDirection, Ltr, Rtl);

impl_enum_fmt!(StyleWhiteSpace, Normal, Pre, Nowrap);

impl_enum_fmt!(StyleVisibility, Visible, Hidden, Collapse);

impl_enum_fmt!(LayoutWritingMode, HorizontalTb, VerticalRl, VerticalLr);

impl_enum_fmt!(LayoutClear, None, Left, Right, Both);

impl_enum_fmt!(
    StyleCursor,
    Alias,
    AllScroll,
    Cell,
    ColResize,
    ContextMenu,
    Copy,
    Crosshair,
    Default,
    EResize,
    EwResize,
    Grab,
    Grabbing,
    Help,
    Move,
    NResize,
    NsResize,
    NeswResize,
    NwseResize,
    Pointer,
    Progress,
    RowResize,
    SResize,
    SeResize,
    Text,
    Unset,
    VerticalText,
    WResize,
    Wait,
    ZoomIn,
    ZoomOut
);

impl_enum_fmt!(
    BorderStyle,
    None,
    Solid,
    Double,
    Dotted,
    Dashed,
    Hidden,
    Groove,
    Ridge,
    Inset,
    Outset
);

impl_enum_fmt!(StyleBackgroundRepeat, NoRepeat, Repeat, RepeatX, RepeatY);

impl_enum_fmt!(
    LayoutDisplay,
    None,
    Block,
    Inline,
    InlineBlock,
    Flex,
    InlineFlex,
    Table,
    InlineTable,
    TableRowGroup,
    TableHeaderGroup,
    TableFooterGroup,
    TableRow,
    TableColumnGroup,
    TableColumn,
    TableCell,
    TableCaption,
    ListItem,
    RunIn,
    Marker,
    Grid,
    InlineGrid,
    FlowRoot
);

impl_enum_fmt!(LayoutFloat, Left, Right, None);

impl_enum_fmt!(LayoutBoxSizing, ContentBox, BorderBox);

impl_enum_fmt!(LayoutFlexDirection, Row, RowReverse, Column, ColumnReverse);

impl_enum_fmt!(LayoutFlexWrap, Wrap, NoWrap, WrapReverse);

impl_enum_fmt!(
    LayoutJustifyContent,
    Start,
    End,
    FlexStart,
    FlexEnd,
    Center,
    SpaceBetween,
    SpaceAround,
    SpaceEvenly
);

impl_enum_fmt!(LayoutAlignItems, Stretch, Center, Start, End, Baseline);

impl_enum_fmt!(
    LayoutAlignContent,
    Start,
    End,
    Stretch,
    Center,
    SpaceBetween,
    SpaceAround
);

impl_enum_fmt!(Shape, Circle, Ellipse);

impl_enum_fmt!(LayoutOverflow, Auto, Scroll, Visible, Hidden, Clip);

impl_enum_fmt!(StyleTextAlign, Center, Left, Right, Justify, Start, End);

impl_enum_fmt!(StyleUserSelect, Auto, Text, None, All);

impl_enum_fmt!(StyleTextDecoration, None, Underline, Overline, LineThrough);

impl_enum_fmt!(
    DirectionCorner,
    Right,
    Left,
    Top,
    Bottom,
    TopRight,
    TopLeft,
    BottomRight,
    BottomLeft
);

impl_enum_fmt!(ExtendMode, Clamp, Repeat);

impl_enum_fmt!(StyleBackfaceVisibility, Visible, Hidden);

// Complex type implementations

fn format_style_background_size(c: &StyleBackgroundSize) -> String {
    match c {
        StyleBackgroundSize::Contain => String::from("StyleBackgroundSize::Contain"),
        StyleBackgroundSize::Cover => String::from("StyleBackgroundSize::Cover"),
        StyleBackgroundSize::ExactSize([w, h]) => format!(
            "StyleBackgroundSize::ExactSize([{}, {}])",
            format_pixel_value(w),
            format_pixel_value(h)
        ),
    }
}

// Scrollbar-related impls moved to `props/style/scrollbar.rs`

pub fn format_scrollbar_info(s: &ScrollbarInfo, tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    let t1 = String::from("    ").repeat(tabs + 1);
    format!(
        "ScrollbarInfo {{\r\n{}width: {},\r\n{}padding_left: {},\r\n{}padding_right: \
         {},\r\n{}track: {},\r\n{}thumb: {},\r\n{}button: {},\r\n{}button: {},\r\n{}resizer: \
         {},\r\n{}}}",
        t1,
        s.width.format_as_rust_code(tabs + 1),
        t1,
        s.padding_left.format_as_rust_code(tabs + 1),
        t1,
        s.padding_right.format_as_rust_code(tabs + 1),
        t1,
        format_style_background_content(&s.track, tabs + 1),
        t1,
        format_style_background_content(&s.thumb, tabs + 1),
        t1,
        format_style_background_content(&s.button, tabs + 1),
        t1,
        format_style_background_content(&s.corner, tabs + 1),
        t1,
        format_style_background_content(&s.resizer, tabs + 1),
        t
    )
}

// Background/filter vec impls moved to their respective modules.

fn format_style_background_content(content: &StyleBackgroundContent, tabs: usize) -> String {
    match content {
        StyleBackgroundContent::LinearGradient(l) => format!(
            "StyleBackgroundContent::LinearGradient({})",
            format_linear_gradient(l, tabs)
        ),
        StyleBackgroundContent::RadialGradient(r) => format!(
            "StyleBackgroundContent::RadialGradient({})",
            format_radial_gradient(r, tabs)
        ),
        StyleBackgroundContent::ConicGradient(r) => format!(
            "StyleBackgroundContent::ConicGradient({})",
            format_conic_gradient(r, tabs)
        ),
        StyleBackgroundContent::Image(id) => format!("StyleBackgroundContent::Image({:?})", id),
        StyleBackgroundContent::Color(c) => {
            format!("StyleBackgroundContent::Color({})", format_color_value(c))
        }
    }
}

fn format_direction(d: &Direction, tabs: usize) -> String {
    match d {
        Direction::Angle(fv) => format!("Direction::Angle({})", format_angle_value(fv)),
        Direction::FromTo(DirectionCorners { from, to }) => format!(
            "Direction::FromTo(DirectionCorners {{ from: {}, to: {} }})",
            from.format_as_rust_code(tabs + 1),
            to.format_as_rust_code(tabs + 1)
        ),
    }
}

fn format_linear_gradient(l: &LinearGradient, tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    let t1 = String::from("    ").repeat(tabs + 1);
    format!(
        "LinearGradient {{\r\n{}direction: {},\r\n{}extend_mode: {},\r\n{}stops: \
         NormalizedLinearColorStopVec::from_const_slice(LINEAR_COLOR_STOP_{}_ITEMS),\r\n{}}}",
        t1,
        format_direction(&l.direction, tabs + 1),
        t1,
        l.extend_mode.format_as_rust_code(tabs + 1),
        t1,
        l.stops.get_hash(),
        t,
    )
}

fn format_conic_gradient(r: &ConicGradient, tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    let t1 = String::from("    ").repeat(tabs + 1);

    format!(
        "ConicGradient {{\r\n{}extend_mode: {},\r\n{}center: {},\r\n{}angle: {},\r\n{}stops: \
         NormalizedRadialColorStopVec::from_const_slice(RADIAL_COLOR_STOP_{}_ITEMS),\r\n{}}}",
        t1,
        r.extend_mode.format_as_rust_code(tabs + 1),
        t1,
        format_style_background_position(&r.center, tabs + 1),
        t1,
        format_angle_value(&r.angle),
        t1,
        r.stops.get_hash(),
        t,
    )
}

fn format_radial_gradient(r: &RadialGradient, tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    let t1 = String::from("    ").repeat(tabs + 1);
    format!(
        "RadialGradient {{\r\n{}shape: {},\r\n{}extend_mode: {},\r\n{}position: {},\r\n{}size: \
         RadialGradientSize::{:?},\r\n{}stops: \
         NormalizedLinearColorStopVec::from_const_slice(LINEAR_COLOR_STOP_{}_ITEMS),\r\n{}}}",
        t1,
        r.shape.format_as_rust_code(tabs + 1),
        t1,
        r.extend_mode.format_as_rust_code(tabs + 1),
        t1,
        format_style_background_position(&r.position, tabs + 1),
        t1,
        r.size,
        t1,
        r.stops.get_hash(),
        t,
    )
}

fn format_linear_color_stops(stops: &[NormalizedLinearColorStop], tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    stops
        .iter()
        .map(|s| format_linear_color_stop(s))
        .collect::<Vec<_>>()
        .join(&format!(",\r\n{}", t))
}

fn format_linear_color_stop(g: &NormalizedLinearColorStop) -> String {
    format!(
        "NormalizedLinearColorStop {{ offset: {}, color: {} }}",
        format_percentage_value(&g.offset),
        format_color_value(&g.color),
    )
}

fn format_radial_color_stops(stops: &[NormalizedRadialColorStop], tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    stops
        .iter()
        .map(|s| format_radial_color_stop(s))
        .collect::<Vec<_>>()
        .join(&format!(",\r\n{}", t))
}

fn format_radial_color_stop(g: &NormalizedRadialColorStop) -> String {
    format!(
        "RadialColorStop {{ angle: {}, color: {} }}",
        format_angle_value(&g.angle),
        format_color_value(&g.color),
    )
}

fn format_style_filter(st: &StyleFilter, tabs: usize) -> String {
    let tabs_minus_one = String::from("    ").repeat(tabs);
    let tabs_str = String::from("    ").repeat(tabs + 1);
    match st {
        StyleFilter::Blend(mb) => format!("StyleFilter::Blend({})", mb.format_as_rust_code(tabs)),
        StyleFilter::Flood(c) => format!("StyleFilter::Flood({})", format_color_value(c)),
        StyleFilter::Blur(m) => format!(
            "StyleFilter::Blur(StyleBlur {{ width: {}, height: {} }})",
            format_pixel_value(&m.width),
            format_pixel_value(&m.height)
        ),
        StyleFilter::Opacity(pct) => {
            format!("StyleFilter::Opacity({})", format_percentage_value(pct))
        }
        StyleFilter::ColorMatrix(cm) => format!(
            "StyleFilter::ColorMatrix(StyleColorMatrix {{ matrix: {} }})",
            cm.matrix
                .iter()
                .map(|f| format_float_value(f))
                .collect::<Vec<_>>()
                .join(&format!(",\r\n{}", tabs_str))
        ),
        StyleFilter::DropShadow(m) => {
            format!("StyleFilter::DropShadow({})", m.format_as_rust_code(tabs))
        }
        StyleFilter::ComponentTransfer => format!("StyleFilter::ComponentTransfer"),
        StyleFilter::Offset(o) => format!(
            "StyleFilter::Offset(StyleFilterOffset {{ x: {}, y: {} }})",
            format_pixel_value(&o.x),
            format_pixel_value(&o.y)
        ),
        StyleFilter::Composite(StyleCompositeFilter::Over) => {
            format!("StyleFilter::Composite(StyleCompositeFilter::Over)")
        }
        StyleFilter::Composite(StyleCompositeFilter::In) => {
            format!("StyleFilter::Composite(StyleCompositeFilter::In)")
        }
        StyleFilter::Composite(StyleCompositeFilter::Atop) => {
            format!("StyleFilter::Composite(StyleCompositeFilter::Atop)")
        }
        StyleFilter::Composite(StyleCompositeFilter::Out) => {
            format!("StyleFilter::Composite(StyleCompositeFilter::Out)")
        }
        StyleFilter::Composite(StyleCompositeFilter::Xor) => {
            format!("StyleFilter::Composite(StyleCompositeFilter::Xor)")
        }
        StyleFilter::Composite(StyleCompositeFilter::Lighter) => {
            format!("StyleFilter::Composite(StyleCompositeFilter::Lighter)")
        }
        StyleFilter::Composite(StyleCompositeFilter::Arithmetic(fv)) => format!(
            "StyleFilter::Composite(StyleCompositeFilter::Arithmetic({}))",
            fv.iter()
                .map(|f| format_float_value(f))
                .collect::<Vec<_>>()
                .join(&format!(",\r\n{}", tabs_minus_one))
        ),
    }
}

fn format_style_transforms(stops: &[StyleTransform], tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    stops
        .iter()
        .map(|s| format_style_transform(s, tabs))
        .collect::<Vec<_>>()
        .join(&format!(",\r\n{}", t))
}

fn format_style_transform(st: &StyleTransform, tabs: usize) -> String {
    let tabs_minus_one = String::from("    ").repeat(tabs);
    let tabs = String::from("    ").repeat(tabs + 1);
    match st {
        StyleTransform::Matrix(m) => format!(
            "StyleTransform::Matrix(StyleTransformMatrix2D {{ a: {}, b: {}, c: {}, d: {}, tx: {}, \
             ty: {} }})",
            format_float_value(&m.a),
            format_float_value(&m.b),
            format_float_value(&m.c),
            format_float_value(&m.d),
            format_float_value(&m.tx),
            format_float_value(&m.ty)
        ),
        StyleTransform::Matrix3D(m) => format!(
            "StyleTransform::Matrix3D(StyleTransformMatrix3D {{\r\n{tabs}m11: {},\r\n{tabs}m12: \
             {},\r\n{tabs}m13: {},\r\n{tabs}m14: {},\r\n{tabs}m21: {},\r\n{tabs}m22: \
             {},\r\n{tabs}m23: {},\r\n{tabs}m24: {},\r\n{tabs}m31: {},\r\n{tabs}m32: \
             {},\r\n{tabs}m33: {},\r\n{tabs}m34: {},\r\n{tabs}m41: {},\r\n{tabs}m42: \
             {},\r\n{tabs}m43: {},\r\n{tabs}m44: {}\r\n{tabs_minus_one}}})",
            format_float_value(&m.m11),
            format_float_value(&m.m12),
            format_float_value(&m.m13),
            format_float_value(&m.m14),
            format_float_value(&m.m21),
            format_float_value(&m.m22),
            format_float_value(&m.m23),
            format_float_value(&m.m24),
            format_float_value(&m.m31),
            format_float_value(&m.m32),
            format_float_value(&m.m33),
            format_float_value(&m.m34),
            format_float_value(&m.m41),
            format_float_value(&m.m42),
            format_float_value(&m.m43),
            format_float_value(&m.m44),
            tabs = tabs,
            tabs_minus_one = tabs_minus_one,
        ),
        StyleTransform::Translate(t) => format!(
            "StyleTransform::Translate(StyleTransformTranslate2D {{ x: {}, y: {} }})",
            format_pixel_value(&t.x),
            format_pixel_value(&t.y)
        ),
        StyleTransform::Translate3D(t) => format!(
            "StyleTransform::Translate3D(StyleTransformTranslate3D {{ x: {}, y: {}, z: {})",
            format_pixel_value(&t.x),
            format_pixel_value(&t.y),
            format_pixel_value(&t.z)
        ),
        StyleTransform::TranslateX(x) => {
            format!("StyleTransform::TranslateX({})", format_pixel_value(&x))
        }
        StyleTransform::TranslateY(y) => {
            format!("StyleTransform::TranslateY({})", format_pixel_value(&y))
        }
        StyleTransform::TranslateZ(z) => {
            format!("StyleTransform::TranslateZ({})", format_pixel_value(&z))
        }
        StyleTransform::Rotate(r) => format!("StyleTransform::Rotate({})", format_angle_value(&r)),
        StyleTransform::Rotate3D(r) => format!(
            "StyleTransform::Rotate3D(StyleTransformRotate3D {{ {}, {}, {}, {} }})",
            format_float_value(&r.x),
            format_float_value(&r.y),
            format_float_value(&r.z),
            format_angle_value(&r.angle)
        ),
        StyleTransform::RotateX(x) => {
            format!("StyleTransform::RotateX({})", format_angle_value(&x))
        }
        StyleTransform::RotateY(y) => {
            format!("StyleTransform::RotateY({})", format_angle_value(&y))
        }
        StyleTransform::RotateZ(z) => {
            format!("StyleTransform::RotateZ({})", format_angle_value(&z))
        }
        StyleTransform::Scale(s) => format!(
            "StyleTransform::Scale(StyleTransformScale2D {{ x: {}, y: {} }})",
            format_float_value(&s.x),
            format_float_value(&s.y)
        ),
        StyleTransform::Scale3D(s) => format!(
            "StyleTransform::Scale3D(StyleTransformScale3D {{ x; {}, y: {}, z: {} }})",
            format_float_value(&s.x),
            format_float_value(&s.y),
            format_float_value(&s.z)
        ),
        StyleTransform::ScaleX(x) => {
            format!("StyleTransform::ScaleX({})", format_percentage_value(&x))
        }
        StyleTransform::ScaleY(y) => {
            format!("StyleTransform::ScaleY({})", format_percentage_value(&y))
        }
        StyleTransform::ScaleZ(z) => {
            format!("StyleTransform::ScaleZ({})", format_percentage_value(&z))
        }
        StyleTransform::Skew(sk) => format!(
            "StyleTransform::Skew(StyleTransformSkew2D {{ x: {}, y: {} }})",
            format_angle_value(&sk.x),
            format_angle_value(&sk.y)
        ),
        StyleTransform::SkewX(x) => {
            format!("StyleTransform::SkewX({})", format_angle_value(&x))
        }
        StyleTransform::SkewY(y) => {
            format!("StyleTransform::SkewY({})", format_angle_value(&y))
        }
        StyleTransform::Perspective(dist) => {
            format!("StyleTransform::Perspective({})", format_pixel_value(&dist))
        }
    }
}

fn format_font_ids(font_ids: &[StyleFontFamily], tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    font_ids
        .iter()
        .map(|s| format!("{}", s.format_as_rust_code(tabs + 1)))
        .collect::<Vec<_>>()
        .join(&format!(",\r\n{}", t))
}

fn format_style_background_position(b: &StyleBackgroundPosition, tabs: usize) -> String {
    let t = String::from("    ").repeat(tabs);
    let t1 = String::from("    ").repeat(tabs + 1);
    format!(
        "StyleBackgroundPosition {{\r\n{}horizontal: {},\r\n{}vertical: {},\r\n{}}}",
        t1,
        format_background_position_horizontal(&b.horizontal),
        t1,
        format_background_position_vertical(&b.vertical),
        t
    )
}

fn format_background_position_horizontal(b: &BackgroundPositionHorizontal) -> String {
    match b {
        BackgroundPositionHorizontal::Left => format!("BackgroundPositionHorizontal::Left"),
        BackgroundPositionHorizontal::Center => format!("BackgroundPositionHorizontal::Center"),
        BackgroundPositionHorizontal::Right => format!("BackgroundPositionHorizontal::Right"),
        BackgroundPositionHorizontal::Exact(p) => format!(
            "BackgroundPositionHorizontal::Exact({})",
            format_pixel_value(p)
        ),
    }
}

fn format_background_position_vertical(b: &BackgroundPositionVertical) -> String {
    match b {
        BackgroundPositionVertical::Top => format!("BackgroundPositionVertical::Top"),
        BackgroundPositionVertical::Center => format!("BackgroundPositionVertical::Center"),
        BackgroundPositionVertical::Bottom => format!("BackgroundPositionVertical::Bottom"),
        BackgroundPositionVertical::Exact(p) => format!(
            "BackgroundPositionVertical::Exact({})",
            format_pixel_value(p)
        ),
    }
}

// Border side style impls moved to `props/style/border.rs`.

// Several small impls were moved to their respective modules.

// StyleTransformOrigin impl moved to `props/style/transform.rs`.

// Implementations for small property types were moved to their respective
// modules to keep the implementations next to the type definitions.
